package org.catspaw.cherubim.persistence.hibernate;

import java.io.Serializable;
import java.util.Iterator;
import java.util.List;

import org.catspaw.cherubim.persistence.PersistenceException;
import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.criterion.DetachedCriteria;

/**
 * hql执行器
 * @author 孙宁振
 */
public class HibernateExecutor {

	public Iterator<?> iterate(final String hql, final Object... params) {
		return (Iterator<?>) execute(new HibernateCallback() {

			@Override
			public Object doExecute(Session session) throws HibernateException {
				Query query = session.createQuery(hql);
				for (int i = 0; i < params.length; i++) {
					query.setParameter(i, params[i]);
				}
				return query.iterate();
			}
		});
	}

	public List<?> find(final String hql, final Object... params) {
		return (List<?>) execute(new HibernateCallback() {

			@Override
			public Object doExecute(Session session) throws HibernateException {
				Query query = session.createQuery(hql);
				for (int i = 0; i < params.length; i++) {
					query.setParameter(i, params[i]);
				}
				return query.list();
			}
		});
	}

	public List<?> find(final String hql, final int offset, final int max, final Object... params) {
		return (List<?>) execute(new HibernateCallback() {

			@Override
			public Object doExecute(Session session) throws HibernateException {
				Query query = session.createQuery(hql);
				for (int i = 0; i < params.length; i++) {
					query.setParameter(i, params[i]);
				}
				query.setFirstResult(offset);
				query.setMaxResults(max);
				return query.list();
			}
		});
	}

	public List<?> find(final DetachedCriteria criteria) {
		return (List<?>) execute(new HibernateCallback() {

			@Override
			public Object doExecute(Session session) throws HibernateException {
				return criteria.getExecutableCriteria(session).list();
			}
		});
	}

	public List<?> find(final DetachedCriteria criteria, final int offset, final int max) {
		return (List<?>) execute(new HibernateCallback() {

			@Override
			public Object doExecute(Session session) throws HibernateException {
				Criteria executableCriteria = criteria.getExecutableCriteria(session);
				executableCriteria.setFirstResult(offset);
				executableCriteria.setMaxResults(max);
				return executableCriteria.list();
			}
		});
	}

	public Object get(final Class<?> clazz, final Serializable id) throws PersistenceException {
		return execute(new HibernateCallback() {

			@Override
			public Object doExecute(Session session) throws HibernateException {
				return session.get(clazz, id);
			}
		});
	}

	public Object get(final String name, final Serializable id) throws PersistenceException {
		return execute(new HibernateCallback() {

			@Override
			public Object doExecute(Session session) throws HibernateException {
				return session.get(name, id);
			}
		});
	}

	public Object save(final Object entity) throws PersistenceException {
		return execute(new HibernateCallback() {

			@Override
			public Object doExecute(Session session) throws HibernateException {
				return session.save(entity);
			}
		});
	}

	public void update(final Object entity) throws PersistenceException {
		execute(new HibernateCallback() {

			@Override
			public Object doExecute(Session session) throws HibernateException {
				session.update(entity);
				return null;
			}
		});
	}

	public void delete(final Object entity) throws PersistenceException {
		execute(new HibernateCallback() {

			@Override
			public Object doExecute(Session session) throws HibernateException {
				session.delete(entity);
				return null;
			}
		});
	}

	public Object load(final Class<?> clazz, final Serializable id) {
		return execute(new HibernateCallback() {

			@Override
			public Object doExecute(Session session) throws HibernateException {
				return session.load(clazz, id);
			}
		});
	}

	public Object load(final String name, final Serializable id) {
		return execute(new HibernateCallback() {

			@Override
			public Object doExecute(Session session) throws HibernateException {
				return session.load(name, id);
			}
		});
	}

	public Object merge(final Object entity) {
		return execute(new HibernateCallback() {

			@Override
			public Object doExecute(Session session) throws HibernateException {
				return session.merge(entity);
			}
		});
	}

	public void persist(final Object entity) {
		execute(new HibernateCallback() {

			@Override
			public Object doExecute(Session session) throws HibernateException {
				session.persist(entity);
				return null;
			}
		});
	}

	public void refresh(final Object entity) {
		execute(new HibernateCallback() {

			@Override
			public Object doExecute(Session session) throws HibernateException {
				session.refresh(entity);
				return null;
			}
		});
	}

	public void saveOrUpdate(final Object entity) {
		execute(new HibernateCallback() {

			@Override
			public Object doExecute(Session session) throws HibernateException {
				session.saveOrUpdate(entity);
				return null;
			}
		});
	}

	public void clear() {
		execute(new HibernateCallback() {

			@Override
			public Object doExecute(Session session) throws HibernateException {
				session.clear();
				return null;
			}
		});
	}

	public void evict(final Object entity) {
		execute(new HibernateCallback() {

			@Override
			public Object doExecute(Session session) throws HibernateException {
				session.evict(entity);
				return null;
			}
		});
	}

	public void evictFromSessionFactory(Class<?> clazz) {
		HibernateCacheUtils.evictFromSessionFactory(clazz);
	}

	public void flush() {
		execute(new HibernateCallback() {

			@Override
			public Object doExecute(Session session) throws HibernateException {
				session.flush();
				return null;
			}
		});
	}

	public int bulkUpdate(final String hql, final Object... params) throws PersistenceException {
		return (Integer) execute(new HibernateCallback() {

			@Override
			public Object doExecute(Session session) throws HibernateException {
				Query query = session.createQuery(hql);
				for (int i = 0; i < params.length; i++) {
					query.setParameter(i, params[i]);
				}
				return query.executeUpdate();
			}
		});
	}

	public int[] bulkUpdate(final String hql, final Object[][] params) throws PersistenceException {
		int[] is = (int[]) execute(new HibernateCallback() {

			@Override
			public Object doExecute(Session session) throws HibernateException {
				int[] ret = new int[params.length];
				for (int i = 0; i < params.length; i++) {
					ret[i] = bulkUpdate(hql, params[i]);
				}
				return ret;
			}
		});
		return is;
	}

	public Object execute(HibernateCallback callback) throws PersistenceException {
		Session session = SessionManager.getInstance().getCurrentSession();
		if (session != null) {
			return callback.execute(session);
		}
		session = SessionManager.getInstance().openSession();
		session.beginTransaction();
		try {
			Object ret = callback.execute(session);
			session.getTransaction().commit();
			return ret;
		} catch (HibernateException e) {
			session.getTransaction().rollback();
			throw new PersistenceException(e);
		} finally {
			if (session != null && session.isOpen()) {
				session.close();
			}
		}
	}
}
